﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Diagnostics;
using System.IO;

using Leap;

class SampleListener : Listener
{
    private Object thisLock = new Object();

    private void SafeWriteLine(String line)
    {
        lock (thisLock)
        {
            Console.WriteLine(line);
        }
    }

    public override void OnInit(Controller controller)
    {
        SafeWriteLine("Initialized");
    }

    public override void OnConnect(Controller controller)
    {
        SafeWriteLine("Connected");
        controller.EnableGesture(Gesture.GestureType.TYPE_CIRCLE);
        controller.EnableGesture(Gesture.GestureType.TYPE_KEY_TAP);
        controller.EnableGesture(Gesture.GestureType.TYPE_SCREEN_TAP);
        controller.EnableGesture(Gesture.GestureType.TYPE_SWIPE);
    }

    public override void OnDisconnect(Controller controller)
    {
        //Note: not dispatched when running in a debugger.
        SafeWriteLine("Disconnected");
    }

    public override void OnExit(Controller controller)
    {
        SafeWriteLine("Exited");
    }

    public override void OnFrame(Controller controller)
    {
      

        Frame frame = controller.Frame();
        

        foreach(var hand in frame.Hands)
        {
            HandSimpledTo14Variables hand_s = new HandSimpledTo14Variables(hand);
            if (Sample.delegate_fetcher != null)
            {
                Sample.delegate_fetcher(hand_s);
            }

            foreach (var signLanguageConfig in Sample.allSignLanguageConfig)
            {
                if (signLanguageConfig.CheckGesture(hand_s))
                {
                    SafeWriteLine(DateTime.Now.ToString("h:mm:ss ")+signLanguageConfig.Word);
                    break;
                }
            }
        }
        
    }
}

class HandSimpledTo14Variables
{
    //The first digit is the thumb, followed by index finger, middle finger, ring finger, and little finger or pinky. 
    public int[,] bentPercent;
    public int[] gapStatus;
    public enum Gap { Overlaped, Closed, Opened};
    public enum HandJoint {BetweenIntermediateAndProximal, BetweenProximalAndPalm };
    public HandSimpledTo14Variables()
    {
        bentPercent = new int[5,2];
        gapStatus = new int[4];
    }

    public HandSimpledTo14Variables(Hand thisHand):this()
    {
        
        FingerList fingerList = thisHand.Fingers;
        if (!fingerList.IsEmpty)
        {
            //generate each bent percent
            foreach(var finger in fingerList)
            {
                /*
                [][][][]  Distal Phalanges
                [][][][]  Intermediate Phalanges
                [][][][]  Proximal Phalanges
                |       |   Palm, Metacarpals
                |       |/\
                |_________/  */      
                
                Bone boneIntermediate = finger.Bone(Bone.BoneType.TYPE_INTERMEDIATE);
                Bone boneProximal = finger.Bone(Bone.BoneType.TYPE_PROXIMAL);
                Bone boneMetacarpals = finger.Bone(Bone.BoneType.TYPE_METACARPAL);
                double angleIntermediateToProximal = boneIntermediate.Direction.AngleTo(boneProximal.Direction);
                double angleProximalToMetacarpals = boneProximal.Direction.AngleTo(thisHand.Direction);

                double degIntermediateToProximal = 180-(int)(angleIntermediateToProximal / (Math.PI / 2)*90);
                double degProximalToMetacarpals = (int)(angleProximalToMetacarpals / (Math.PI / 2)*90);

                bentPercent[(int)finger.Type, 0] = (int)(((180-degIntermediateToProximal) / 90) * 100);
                bentPercent[(int)finger.Type, 1] = (int)(((180-degProximalToMetacarpals) / 90) * 100);
            }

            //generate gap status
            for(int i = (int)Finger.FingerType.TYPE_THUMB; i < (int)Finger.FingerType.TYPE_PINKY; i++)
            {
                Bone boneFingerAProximal = fingerList[i].Bone(Bone.BoneType.TYPE_PROXIMAL);
                Bone boneFingerBProximal = fingerList[i+1].Bone(Bone.BoneType.TYPE_PROXIMAL);
                //Solve bone's projection on palm

                //1. solve length of bone's projection on palm plain's normal    
                float boneAProjection = boneFingerAProximal.Direction.Dot(thisHand.Direction.Normalized);
                float boneBProjection = boneFingerBProximal.Direction.Dot(thisHand.Direction.Normalized);

                //2.generate vector parallel to palm
                Vector boneAParalleledToPalm = boneFingerAProximal.Direction + boneAProjection * thisHand.Direction.Normalized;
                Vector boneBParalleledToPalm = boneFingerBProximal.Direction + boneBProjection * thisHand.Direction.Normalized;

                double degOfGap = boneAParalleledToPalm.AngleTo(boneBParalleledToPalm) / (Math.PI / 2) * 90;
                Debug.WriteLine(degOfGap);
             
            }

            foreach(var percent in bentPercent)
            {
                Debug.Write(percent.ToString()+" ");
            }
            Debug.WriteLine("");
        }
    }
}

class SignLanguageConfig
{
    public string Word;
    public int[,] BentPercentExpectation;
    public int[,] BentPercentPrecision;
    bool IsInitialized;

    public SignLanguageConfig()
    {
        BentPercentExpectation = new int[5, 2];
        BentPercentPrecision = new int[5, 2];
        IsInitialized = false;
    }

    public SignLanguageConfig(string fileName, string word):this()
    {
        Word = word;
        InitFromConfigFile(fileName);
    }



    void InitFromConfigFile(string fileName)
    {
        //data format: 5 fingers, each value is present as <precise value> <precision>
        List<string> data = FileReader.GetContentWithoutComment(fileName);
        if (data.Count == 5)
        {
            for(int i = 0; i < data.Count; i++)
            {
                try
                {
                    string[] fourValues = data[i].Split(' ');
                    if (fourValues.Count() >= 4)
                    {
                        BentPercentExpectation[i, 0] = int.Parse(fourValues[0]);
                        BentPercentPrecision[i, 0] = int.Parse(fourValues[1]);
                        BentPercentExpectation[i, 1] = int.Parse(fourValues[2]);
                        BentPercentPrecision[i, 1] = int.Parse(fourValues[3]);
                    }
                    else return;
                }
                catch
                {
                    return;
                }
            }
            IsInitialized = true;
        }
    }

    public bool CheckGesture(HandSimpledTo14Variables hand_s)
    {
        if (!IsInitialized) return false;
        for (int i = 0; i < 5; i++)
        {
            if(!(hand_s.bentPercent[i, 0] >= BentPercentExpectation[i, 0] - BentPercentPrecision[i, 0] &&
                hand_s.bentPercent[i, 0] <= BentPercentExpectation[i, 0] + BentPercentPrecision[i, 0] &&
                hand_s.bentPercent[i, 1] >= BentPercentExpectation[i, 1] - BentPercentPrecision[i, 1] &&
                hand_s.bentPercent[i, 1] <= BentPercentExpectation[i, 1] + BentPercentPrecision[i, 1]))
            {
                return false;
            }
        }
        return true;
    }
}

class FileReader
{
    public static List<string> GetContentWithoutComment(string fileName)
    {
        List<string> result = new List<string>();
        try
        {
            using (StreamReader streamReader = new StreamReader(fileName))
            {
                string line;
                while((line = streamReader.ReadLine()) != null)
                {
                    if (line[0] != '#')
                    {
                        result.Add(line);
                    }
                }
            }
        }
        catch { }
        return result;
    }
}

class Sample
{
    public static List<string> configList;
    public static List<SignLanguageConfig> allSignLanguageConfig;

    public delegate void delegate_HandInfoFetcher(HandSimpledTo14Variables hand);
    public static delegate_HandInfoFetcher delegate_fetcher;

    public static void setDelegare_FetchHandInfo(delegate_HandInfoFetcher dele)
    {
        delegate_fetcher = dele;
    }

    public static void Main()
    {
        // Create a sample listener and controller
        SampleListener listener = new SampleListener();
        Controller controller = new Controller();

        configList = FileReader.GetContentWithoutComment("config_list.txt");
        allSignLanguageConfig = new List<SignLanguageConfig>();
        foreach(string config in configList)
        {
            allSignLanguageConfig.Add(new SignLanguageConfig(config, config.Split('.')[0].Replace("configs/","").Replace('/','.')));
        }
        

        // Have the sample listener receive events from the controller
        controller.AddListener(listener);


        // Keep this process running until Enter is pressed
        Console.WriteLine("Press Enter to quit...");
        Console.ReadLine();

        // Remove the sample listener when done
        controller.RemoveListener(listener);
        controller.Dispose();
    }
}
